package de.ama.crawler;

import com.github.savvasdalkitsis.jtmdb.*;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import de.ama.search.WordIndex;
import de.ama.util.Util;
import de.ama.view.AppWindow;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.RenderedImage;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * Created by IntelliJ IDEA.
 * User: ama
 * Date: 14.02.11
 * Time: 18:05
 * To change this template use File | Settings | File Templates.
 */
public class Store {
    private File dir = null;
    private boolean dirty = false;
    private boolean loaded = false;
    private int storeCount = 0;

    private List<Entry> entries = new ArrayList<Entry>();

    private XStream xstream = new XStream(new DomDriver());
    private static Store theInstance;

    public Store() {

        theInstance = this;
        xstream.alias("movie", Entry.class);
        this.dir = Config.get().getDataDir();
        this.dir.mkdirs();

        read();
    }

    public static Store get() {
        return theInstance;
    }

    public synchronized void addMovie(File file) {
        Entry entry = findMovieByPath(Entry.normalizePath(file.getAbsolutePath()));
        if (entry == null) {
            entry = new Entry(file.getAbsolutePath());
            entries.add(entry);
            AppWindow.get().getTableModel().addEntry(entry);
            Log.log("Store.addMovie " + entry.getPath());
            dirty = true;
        }
        if(entry.getTime()==0){
            entry.setTime(System.currentTimeMillis());
            dirty = true;
        }
    }

    public synchronized void replaceMovie(Entry old, Entry replace) {
        int i = entries.indexOf(old);
        if (i >= 0) {
            entries.set(i, replace);
            AppWindow.get().getTableModel().replaceEntry(old, replace);
            buildIndex(replace);
        }
        dirty=true;
    }

    public Entry findMovieByPath(String key) {
        for (Entry e : entries) {
            if (key.equals(e.getPath())) {
                return e;
            }
        }
        return null;
    }

    public Entry findMovieById(int id) {
        for (Entry e : entries) {
            if (id == e.getId()) {
                return e;
            }
        }
        return null;
    }


    public void cleanEntrieWithoutPath() {
        List<Entry> garbage = new ArrayList<Entry>();
        for (Entry e :entries) {
            if(Util.isEmpty(e.getPath())){
                garbage.add(e);
            } else {
                String fileExtension = Util.getFileExtension(e.getPath()).toLowerCase();
                if(!Config.get().getTypes().contains(fileExtension)){
                    garbage.add(e);
                }
            }
        }

        entries.removeAll(garbage);

        Log.log("removed " + garbage.size() + " invalid entries");
    }

    public void save() {
        try {
            storeCount++;
            if(storeCount>4)
                storeCount=0;

            File dbFile = new File(dir, "!movies"+storeCount+".xml");

            FileOutputStream out = new FileOutputStream(dbFile);
            xstream.toXML(entries, new OutputStreamWriter(out,"UTF-8"));

            Log.log("Store.saved " + entries.size() + " tableModel to "+dbFile.getName());
            dirty = false;
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void read() {
        try {
            File dbFile = null;
            long m=0;
            for(int i= 0;i<4;i++ ){
                File f = new File(dir, "!movies"+i+".xml");
                if(f.exists() && f.lastModified()>m){
                        m = f.lastModified();
                        dbFile = f;
                }
            }

            entries.clear();

            if(dbFile == null){
                return ;
            }

            InputStream is = new FileInputStream(dbFile);
            entries.addAll((List<Entry>) xstream.fromXML(is));
            Log.log("Store.read " + entries.size() + " tableModel from "+dbFile.getName());
            cleanEntrieWithoutPath();
            dirty = false;
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void updateAllEntrys() {

        new Thread("update store"){
            @Override
            public void run() {
                List<Entry> unknown = new ArrayList<Entry>();
                for (Entry entry : entries) {
                    if (!entry.isQueried()) {
                        unknown.add(entry);
                    }
                    buildIndex(entry);
                }

                int count = 1;
                for (Entry entry : unknown) {
                    AppWindow.get().showTitle("updating  ["+count+"/"+unknown.size()+"] "+ entry.getSearchString());
                    updateEntry(entry);
                    buildIndex(entry);
                    count++;
                }

                loaded = true;
                AppWindow.get().refreshNorth();

            }
        }.start();

    }

    private void buildIndex(Entry entry) {
        WordIndex.get().parseText(entry.getSearchString(),entry.getId());
        WordIndex.get().parseText(entry.getPath(),entry.getId());
        WordIndex.get().parseText(entry.getTitle(),entry.getId());
        WordIndex.get().parseText(entry.getText(),entry.getId());
        WordIndex.get().parseText(entry.getPlot(),entry.getId());
    }

    public void updateEntry(Entry entry) {

        try {
            List<Movie> list = retreiveInfos(entry);
            if (list == null || list.size() == 0) {
                fetchInfos(entry, null, true);
            } else if (list.size() == 1) {
                entry.getMatches().clear();
                fetchInfos(entry, list.get(0),true);
            } else if (list.size() > 1) {
                fetchInfos(entry, list.get(0),true);
                entry.setTitle("? ? ?");
                entry.getMatches().clear();
                for (Movie m : list) {
                    Entry e = new Entry(entry.getPath());
                    fetchInfos(e, m,true);
                    entry.getMatches().add(e);
                }
            }
        } catch (Exception e) {
            Log.log(e);
        }
    }

    private List<Movie> retreiveInfos(Entry entry) {
        List<Movie> list=null;
        String searchString = entry.getSearchString();
        try {
            if(searchString.startsWith("id=")){
                list = new ArrayList<Movie>();
                int id = Integer.parseInt(searchString.substring(3).trim());
                Movie movie = Movie.getInfo(id);
                list.add(movie);
            } else {
                list = Movie.deepSearch(searchString);
            }
        } catch (Exception e) {
//            System.out.println("Parse Error in " + searchString + e.getMessage());
        }
        return list;
    }

    private void fetchInfos(Entry entry, Movie movie, boolean full) {
        entry.setQueried(true);
        dirty = true;
        entry.setText("");
        if (movie == null) {
            entry.setTitle(entry.getPath());
            entry.addText("Path   : ", entry.getPath());
            entry.addText("Suche   : ", entry.getSearchString());
        } else {
            try {
                entry.setTitle(movie.getName());
                if (full) {
                    String lastJob = "";
                    String cast = "";
                    Set<CastInfo> casts = movie.getCast();
                    for (Iterator<CastInfo> iterator = casts.iterator(); iterator.hasNext();) {
                       CastInfo ci = iterator.next();
                       if("Actor".equalsIgnoreCase(ci.getJob())
                                || "Producer".equalsIgnoreCase(ci.getJob())
                                || "Director".equalsIgnoreCase(ci.getJob()) )
                       {
                            String delim = ", ";
                            if(!Util.safeToString(lastJob).equals(ci.getJob())){
                                cast += "\n"+ci.getJob() +" : ";
                                lastJob = ci.getJob();
                                delim = "";
                            }
                            cast += delim + Util.safeToString(ci.getName());
                            if(!Util.isEmpty(ci.getCharacterName())){
                                cast += "("+Util.safeToString(ci.getCharacterName())+")";
                            }
                       }
                    }
                    String genre = "";
                    Set<Genre> genres = movie.getGenres();
                    for (Iterator<Genre> iterator = genres.iterator(); iterator.hasNext();) {
                        Genre g = iterator.next();
                        if(genre.length()>0) genre += ", ";
                        genre += g.getName();
                    }

                    entry.setPlot(movie.getOverview());
                    entry.setId(movie.getID());
                    entry.addText("", Util.safeToString(movie.getOriginalName()) + "   " + movie.getRuntime() + " min");
                    entry.addText("", Util.asString(movie.getReleasedDate())+"   Alter "+Util.safeToString(movie.getCertification()));
                    entry.addText("",genre);
                    entry.addText("",cast);
                }

                MovieImages images = movie.getImages();
                if (images != null) {
                    Iterator<MoviePoster> iterator = images.posters.iterator();
                    if (iterator.hasNext()) {
                        MoviePoster posters = iterator.next();
                        URL url = posters.getSmallestImage();
                        if (!Util.isEmpty(url)) {
                            File file = new File(dir, url.getFile().replace('/','_'));
                            if(!file.exists()){
                               Util.writeCompressedJpeg(Util.inputStream(url), file, 1f);
                            }
                            entry.setImagePath(file.getAbsolutePath());
                        }
                        if (full) {
                            url = posters.getImage(MoviePoster.Size.MID);
                            if (!Util.isEmpty(url)) {
                                File file = new File(dir, url.getFile().replace('/','_'));
                                if(!file.exists()){
                                   Util.writeCompressedJpeg(Util.inputStream(url), file, 1f);
                                }
                                entry.setPosterPath(file.getAbsolutePath());
                            }
                        }
                    }
                }
            } catch (Exception e) {
                Log.log(e);
            }
        }

        AppWindow.get().getTableModel().filterAndSort();
    }


    public List<Entry> getAllEntries() {
        return entries;
    }

    public void setDirty(boolean dirty) {
        this.dirty = dirty;
    }

    public boolean isDirty() {
        return dirty;
    }

    public void removeEntry(Entry entry) {
        entries.remove(entry);
        dirty = true;
    }

    public int getSize() {
        return entries.size();
    }

    public boolean isLoaded() {
        return loaded;
    }

    public void storePoster(Image image, Entry entry) throws IOException {
        File file = new File(dir, entry.calcImageName()+"_Poster.jpg");
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write((RenderedImage) image,"png", os);
        InputStream is = new ByteArrayInputStream(os.toByteArray());
        Util.writeCompressedJpeg(is,file,1.0f);
        entry.setPosterPath(file.getAbsolutePath());


        file = new File(dir, entry.calcImageName()+"_Thumb.jpg");
        os = new ByteArrayOutputStream();
        ImageIO.write((RenderedImage) image,"png", os);
        is = new ByteArrayInputStream(os.toByteArray());
        Util.writeCompressedAndScaledJpeg(is,file,95,135,1.0f);
        entry.setImagePath(file.getAbsolutePath());
    }
}
